import java.io.*;
import java.net.*;
import java.security.KeyStore;

import javax.net.ServerSocketFactory;
import javax.net.ssl.*;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.io.*;
import java.net.*;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;

import javax.net.ssl.SSLSocket;


//import javax.security.cert.X509Certificate;
import java.security.KeyStore;
import java.security.Principal;

import java.security.cert.X509Certificate;

public class NewServer {

	static SSLSocket sslsocket = null;
	static SSLServerSocket sslSocket=null;
	static String FileName=null;
	static String Command=null;
	static String comm=null;
	static int commvalue;
	static String CN;
	static String userDir;
	
	static String commInd[];
	static String direc;


	public static void main(String args[])
	{

		int port_number=2224;
		String tStore="new_server.jks";
		String passp="ashishsh";

		if (args.length < 3)
		{
			System.out.println("Now using port number="+port_number);
		} else {
			port_number=Integer.valueOf(args[0]).intValue();
			tStore=args[1];
			passp=args[2];
		}


		try{
			ServerSocketFactory ssf = getServerSocketFactory("SSL", tStore, passp);
			sslSocket = (SSLServerSocket)ssf.createServerSocket(port_number);
			sslSocket.setNeedClientAuth(true);
			

			System.out.println("\nListening on " + InetAddress.getLocalHost().toString() + ":" + port_number + ".");
			SSLSocket sslSockClient = (SSLSocket) sslSocket.accept();

			
			
			SSLSession session = sslSockClient.getSession();
		    X509Certificate cert;
		    try {
		    	
		      cert = (X509Certificate) session.getPeerCertificates()[0];
		    } catch (SSLPeerUnverifiedException e) {
		      System.err.println(session.getPeerHost() + " did not present a valid certificate.");
		      return;
		    }
		    System.out.println(session.getPeerHost() + " has presented a certificate belonging to:");
		    Principal p = cert.getSubjectDN();
		    
		    int to= p.getName().indexOf("=");
		    int frm=p.getName().indexOf(",");
		    
		     CN=p.getName().substring(to+1, frm);
		    
		     userDir=getdir(CN);
		     if(userDir==null)
		     {
		    	 System.out.println("No Directory");
		    	 System.exit(1);
		    	 
		     }
		    System.out.println("\t[" + p.getName() + "]");
		    System.out.println("The certificate bears the valid signature of:");
		    System.out.println("\t[" + cert.getIssuerDN().getName() + "]");
		    
			System.out.println("Connected to " + sslSockClient.getInetAddress().toString() + "\n");
			BufferedReader cmdReader = new BufferedReader(new InputStreamReader(sslSockClient.getInputStream()));
			BufferedWriter cmdSender = new BufferedWriter(new OutputStreamWriter(sslSockClient.getOutputStream()));


			loop: while(true){
				Command = cmdReader.readLine();
		//	Command=cmds;
				if(!valid())
				{
					//BufferedWriter cmdSender = new BufferedWriter(new OutputStreamWriter(sslSockClient.getOutputStream()));
			//		cmdSender.write("error" + '\n');
				//	cmdSender.flush();
					
				    continue;
				}
				
					//BufferedWriter cmdSender = new BufferedWriter(new OutputStreamWriter(sslSockClient.getOutputStream()));
					//cmdSender.write("ok" + '\n');
					//cmdSender.flush();
					
				
				
				swit: switch(commvalue){
				case 0:
				 {
						String strFileSend = userDir + FileName;		
						
						/*String strFile;
						if(direc!=null)
							{
							strFile=userDir+direc;
							
							}
						else{
							strFile=userDir;
							
						}*/

						BufferedOutputStream bos = new BufferedOutputStream(sslSockClient.getOutputStream());
						if(direc==null){
							System.out.println("\ndirec null" );
						}
						else{
							String strFile=userDir+direc;
							
							File fFile = new File(strFile);			
							if(!fFile.exists())
							{
								System.out.println("\nThe dir '" + strFile + "' does not exist.\n");
								bos.write(LongToBytes(0));
								bos.flush();
								break swit;
							}
							}
						
									
						
						System.out.println("requested file"+strFileSend);
						
						File FileSend = new File(strFileSend);
						long iLen = 0;
						
						if(FileSend.exists())
						   iLen = FileSend.length();

						bos.write(LongToBytes(iLen));
						bos.flush();
						
						if(iLen == 0)
						    break swit;
						
						//System.out.print("Transferring length...");			
						//System.out.println(" Length of file: " + iLen);

						System.out.print("Transferring file " + strFileSend + "...");
						BufferedInputStream bis = new BufferedInputStream(new FileInputStream(strFileSend));
						byte[] bufTemp = new byte[1024];
						int len = 0;
						while((len = bis.read(bufTemp)) > 0)
						{
						    bos.write(bufTemp, 0, len);
						    bos.flush();
						}

						bis.close();
						System.out.print(" Sent!\n\n");
						
						break swit;
					    }
				case 1:
				 {
						String strFileSave = userDir + FileName;
						if(direc==null){
							//System.out.println("\ndirec null" );
						}
						else{
							String strFile=userDir+direc;
							
							File fFile = new File(strFile);			
							if(!fFile.exists())
							{
								System.out.println("\nThe dir '" + strFile + "' does not exist for you.\n");
							
								break swit;
							}
							}
						BufferedInputStream bis = new BufferedInputStream(sslSockClient.getInputStream());
						BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(strFileSave));
						

						//System.out.print("\nReceiving length...");
						byte[] bufLen = new byte[8];
						bis.read(bufLen);
						long fileLen = BytesToLong(bufLen, 0);
						//System.out.println(" Length of file: " + fileLen);

						System.out.print("Receiving file " + FileName + "...");
						byte[] bufTemp = new byte[1024];
						long lRcvd = 0;
						while(lRcvd < fileLen)
						{
						    int len = bis.read(bufTemp);
						    bos.write(bufTemp, 0, len);
						    bos.flush();
						    lRcvd += len;
						}

						bos.close();		    
						System.out.print(" Saved as " + strFileSave + "!\n\n");
						
						break swit;
					    }
				case -1:
					break loop;
					
				case 3:
					break loop;

				default:
					break swit;

				}

			}

			sslSockClient.close();
			sslSocket.close();
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
			e.printStackTrace();

		}

	}

	public static String getdir(String uName){
		boolean find=false;
		String userdir=null;
		try
		{
			FileReader fro = new FileReader( "serverDB.txt" );
			BufferedReader bro = new BufferedReader( fro );

			String stringFromFile = bro.readLine( );

			while( stringFromFile != null || !find) // end of the file
			{
				//System.out.println(stringFromFile);
				find=findMatch(stringFromFile,bro,uName);
				if(find)
				{
					//System.out.println("got something");
					userdir= getMatch(stringFromFile,bro,uName);
					break;
				}
				stringFromFile = bro.readLine( );  // read next line
			}

			bro.close( );
		}

		catch( FileNotFoundException filenotfoundexxption )
		{
			System.out.println( "Server file does not exist" );
		}

		catch( IOException ioexception )
		{
			ioexception.printStackTrace( );
		}
		return userdir;
	}
	
	private static boolean findMatch(String line,BufferedReader bro,String uName){
		String arr[]=line.split(";");
		arr[0]=arr[0].trim();
		if(arr[0].equalsIgnoreCase(uName))
		{
			return true;
		}
		else{
			return false;
		}
	}

	private static String getMatch(String line,BufferedReader bro,String uName){
		String arr[]=line.split(";");
		arr[0]=arr[0].trim();
		arr[1]=arr[1].trim();
		if(arr[0].equalsIgnoreCase(uName))
		{
			return arr[1];
		}
		else{
			return null;
		}
	}
	private static ServerSocketFactory getServerSocketFactory(String type,String tStore,String passp) {
		if (type.equals("SSL")) {
			System.setProperty("javax.net.ssl.trustStore", tStore);
			System.setProperty("javax.net.ssl.trustStorePassword", passp);
			SSLServerSocketFactory ssf = null;
			try {
				// set up key manager to do server authentication
				SSLContext ctx;
				KeyManagerFactory kmf;
				KeyStore ks;
				char[] passphrase = passp.toCharArray();

				ctx = SSLContext.getInstance("SSL");
				kmf = KeyManagerFactory.getInstance("SunX509");
				ks = KeyStore.getInstance("JKS");

				ks.load(new FileInputStream(tStore), passphrase);
				kmf.init(ks, passp.toCharArray());
				ctx.init(kmf.getKeyManagers(), null, null);

				ssf = ctx.getServerSocketFactory();
				return ssf;
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			return ServerSocketFactory.getDefault();
		}
		return null;
	}
	
	public static boolean valid(){
		if(Command.equals(""))
		{
			comm=null;
			commvalue=-1;
			return false;
		}
		commInd = Command.split(" ");
		if((commInd[0].equals("r")||commInd[0].equals("w"))&&commInd.length==2)
		{
			comm=commInd[0];
			if(commInd[0].equals("r"))
				commvalue=0;
			else if(commInd[0].equals("w"))
				commvalue=1;
			else 
				commvalue=-1;


			FileName=commInd[1];
			
			if(commInd[1].contains("/"))
			{
				int beginIndex=commInd[1].lastIndexOf('/');
				String temp=commInd[1].substring(0,beginIndex-1);
				direc=temp;

			}else{
				direc=null;
			}

			return true;
		}
		else{
			if(commInd[0].equals("exit"))
			{
				comm=commInd[0];
				commvalue=3;
				return true;

			}
			else{
				comm=null;
				commvalue=-1;
				return false;
			}
			
		}
	}
	
	 public static byte[] LongToBytes(long n)
	    {
		byte[] bytearr = new byte[8];
		bytearr[7] = (byte) (n);
		n >>>= 8;
		bytearr[6] = (byte) (n);
		n >>>= 8;
		bytearr[5] = (byte) (n);
		n >>>= 8;
		bytearr[4] = (byte) (n);
		n >>>= 8;
		bytearr[3] = (byte) (n);
		n >>>= 8;
		bytearr[2] = (byte) (n);
		n >>>= 8;
		bytearr[1] = (byte) (n);
		n >>>= 8;
		bytearr[0] = (byte) (n);

		return bytearr;
	    }
	    
	    // Converts a byte array into a Long
	    public static long BytesToLong(byte[] b, int o)
	    {
		long value = 0;
		for ( int i=0; i<8; i++ )
		{ 
		    int shift = 7-i;
		    value |= (b[o+i]<<shift);
		}
		return value;
	    }   
}
